package com.mo;

import cn.hutool.core.thread.NamedThreadFactory;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 导出线程池
 *
 * @author moon.pan
 * @program cyberdata
 * @apiNote
 * @since 2022/6/1
 */
@Slf4j
public class ThreadHelper {

    private static final ExecutorService GLOBAL_THREAD_POOL;

    private static final AtomicInteger POOL_SIZE = new AtomicInteger(0);

    static {
        // 线程名称前缀
        String prefix = "cyberdata-template-";

        /**
         * 最大线程数、最小线程数、利用率系数
         */
        float blockingCoefficient = 0.5F;
        int processors = Runtime.getRuntime().availableProcessors();
        int maximumPoolSize = (int) (processors / (1 - blockingCoefficient));


        /**
         * 线程池参数:
         *     核心线程数: cpu的核心数
         *     最大线程数: cpu的核心数/利用率系数
         *     线程最大空闲时间: 默认30
         *     时间单位: 秒
         *     线程等待队列: 链表队列
         *     线程创建工厂: 线程命名工厂
         *     拒绝策略: 用调用者所在的线程来执行任务
         */

        GLOBAL_THREAD_POOL = new ThreadPoolExecutor(processors, maximumPoolSize,
                30L,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(processors * 3),
                new NamedThreadFactory(prefix, false),
                new ThreadPoolExecutor.CallerRunsPolicy());
    }

    /**
     * 提交异步任务
     *
     * @param callable
     * @return
     */
    public static <T> Future<T> submit(Callable<T> callable) {
        POOL_SIZE.incrementAndGet();
        return GLOBAL_THREAD_POOL.submit(() -> {
            try {
                return callable.call();
            } catch (Throwable e) {
                log.error("异步任务出现异常 : {}", e.getMessage(), e);
            } finally {
                POOL_SIZE.decrementAndGet();
            }
            return null;
        });
    }

    @SneakyThrows
    public static void shutdown() {
        GLOBAL_THREAD_POOL.shutdown();
        GLOBAL_THREAD_POOL.awaitTermination(10, TimeUnit.SECONDS);
    }

    /**
     * 获取任务数量
     *
     * @return
     */
    public static int getPoolSize() {
        return POOL_SIZE.get();
    }
}
